/* machine_adc.c */

#include <stdio.h>

#include "machine_adc.h"
#include "hal_adc.h"
#include "hal_gpio.h"

/*
 * Declerations.
 */

/* Local functions. */
STATIC mp_obj_t machine_adc_obj_init_helper(const machine_adc_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

/* Class method, which would be called by class name. */
STATIC     void machine_adc_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind);
STATIC mp_obj_t machine_adc_obj_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
       mp_obj_t machine_adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);


/* Instance methods, which would be called by class instance name. */
STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in);
STATIC mp_obj_t machine_adc_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args);

/*
 * Functions.
 */

/* adc.read_u16() */
STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in)
{
    /* self_in is machine_pin_obj_t. */
    machine_adc_obj_t * self = (machine_adc_obj_t *)self_in;

    return MP_OBJ_NEW_SMALL_INT(ADC_GetChnConvResult(self->adc_port, self->adc_channel, NULL));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_u16_obj, machine_adc_read_u16);

STATIC mp_obj_t machine_adc_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
    /* args[0] is machine_pin_obj_t. */
    //printf("machine_adc_init().\r\n");
    return machine_adc_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_adc_init_obj, 1, machine_adc_init);

/* parameter list. */
typedef enum
{
    ADC_INIT_ARG_MODE = 0,
} machine_adc_init_arg_t;

//STATIC uint32_t adc_working_conv_seq = 0u; /* keep the current working channel in adc conversion sequence. */

STATIC mp_obj_t machine_adc_obj_init_helper (
    const machine_adc_obj_t *self, /* machine_adc_obj_t类型的变量，包含硬件信息 */
    size_t n_args, /* 位置参数数量 */
    const mp_obj_t *pos_args, /* 位置参数清单 */
    mp_map_t *kw_args ) /* 关键字参数清单结构体 */
{
    /* 解析参数 */
    static const mp_arg_t allowed_args[] =
    {
        [ADC_INIT_ARG_MODE] { MP_QSTR_init , MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
    };
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    /* 配置引脚复用功能为ADC输入. */
    GPIO_Init_Type gpio_init;
    gpio_init.Speed = GPIO_Speed_50MHz;
    gpio_init.Pins = ( 1u << (self->pin_obj->gpio_pin) );
    gpio_init.PinMode = GPIO_PinMode_In_Analog;
    GPIO_Init(self->pin_obj->gpio_port, &gpio_init);

    /* 当init=0时，重新初始化转换器，并清空转换队列。 */
    if (args[ADC_INIT_ARG_MODE].u_bool)
    {
        /* stop previous conversion. */
        ADC_DoSwTrigger(self->adc_port, false);
        ADC_Enable(self->adc_port, false);

        /* re-init the converter. */
        //adc_working_conv_seq = 0u;
        self->conf->active_channels = 0u; /* 清空转换队列. */

        ADC_Init_Type adc_init;
        adc_init.Resolution = ADC_Resolution_12b;
        adc_init.ClockDiv   = ADC_ClockDiv_8;
        adc_init.Align      = ADC_Align_Left;
        adc_init.ConvMode   = ADC_ConvMode_SeqContinues;
        ADC_Init(self->adc_port, &adc_init);
        ADC_Enable(self->adc_port, true);
    }

    /* register the adc conversion channel. */
    //adc_working_conv_seq |= (1u << self->adc_channel);
    self->conf->active_channels |= (1u << self->adc_channel);

    //printf("ADC_EnableRegularSeq(): 0x%lx\r\n", (uint32_t)adc_working_conv_seq);

    /* add the channel into conv sequence. */
    ADC_RegSeqConf_Type adc_regseq_conf;
    adc_regseq_conf.SeqSlots = self->conf->active_channels; //adc_working_conv_seq;
    adc_regseq_conf.SeqDirection = ADC_RegSeqDirection_LowFirst;
    ADC_EnableRegSeq(self->adc_port, &adc_regseq_conf);
    ADC_SetChnSampleTime(self->adc_port, self->adc_channel, ADC_SampleTime_Alt8);

    ADC_DoSwTrigger(self->adc_port, true);

    return mp_const_none;
}

STATIC     void machine_adc_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind)
{
    /* o is the machine_pin_obj_t. */
    (void)kind;
    const machine_adc_obj_t *self = MP_OBJ_TO_PTR(o);

    uint32_t port_idx = 0u;
    for (port_idx = 0u; port_idx < machine_adc_port_num; port_idx++)
    {
        if (self->adc_port == machine_adc_port[port_idx])
        {
            break;
        }
    }

    //mp_printf(print, "ADC(%d)", channel);
    mp_printf(print, "ADC(%d):%d, on Pin(%s)",
        port_idx * machine_adc_channel_num_per_port + self->adc_channel,
        machine_adc_read_u16(o), qstr_str(self->pin_obj->name));
}

STATIC mp_obj_t machine_adc_obj_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args)
{
    /* self_in is machine_pin_obj_t. */
    mp_arg_check_num(n_args, n_kw, 0, 1, false);
    //machine_pin_obj_t *self = self_in;

    if ( n_args == 0 )  /* read value. */
    {
        return machine_adc_read_u16(self_in);
    }
    else /* write value. */
    {
        return mp_const_none;
    }
}

/* return an instance of machine_adc_obj_t. */
mp_obj_t machine_adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    const machine_adc_obj_t *adc = adc_find(args[0]);

    if ( (n_args >= 1) || (n_kw >= 0) )
    {
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来，单独封装成kw_args。 */
        machine_adc_obj_init_helper(adc, n_args - 1, args + 1, &kw_args);
    }

    return (mp_obj_t)adc;
}


/* class locals_dict_table. */
STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] =
{
    /* Class instance methods. */
    { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&machine_adc_read_u16_obj) },
    { MP_ROM_QSTR(MP_QSTR_init),     MP_ROM_PTR(&machine_adc_init_obj) },

};
STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table);


const mp_obj_type_t machine_adc_type =
{
    { &mp_type_type },
    .name        = MP_QSTR_ADC,
    .print       = machine_adc_obj_print, /* __repr__(), which would be called by print(<ClassName>). */
    .call        = machine_adc_obj_call,  /* __call__(), which can be called as <ClassName>(). */
    .make_new    = machine_adc_obj_make_new, /* create new class instance. */
    .locals_dict = (mp_obj_dict_t *)&machine_adc_locals_dict,
};

/*
 * User functions.
 */
/* 通过本函数都返回一个期望的ADC对象。
 * 传入参数可以是：
 * - 一个已经实例化好的ADC对象
 * - 一个ADC清单中的索引编号
 * - 一个可能绑定ADC通道的Pin对象
 * - 一个可能绑定ADC通道的Pin字符串
 */
const machine_adc_obj_t *adc_find(mp_obj_t user_obj)
{
    /* 如果传入参数本身就是一个ADC的实例，则直接送出这个ADC。 */
    if ( mp_obj_is_type(user_obj, &machine_adc_type) )
    {
        return user_obj;
    }

    /* 如果传入参数是一个ADC通道号，则通过索引在ADC清单中找到这个通道，然后送出这个通道。 */
    if ( mp_obj_is_small_int(user_obj) )
    {
        uint8_t adc_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);
        if ( adc_idx < machine_adc_channel_num )
        {
            return machine_adc_objs[adc_idx];
        }
    }

    /* 如果传入参数本身就是一个Pin的实例，则通过倒排查询找到包含这个Pin对象的ADC通道。 */
    if ( mp_obj_is_type(user_obj, &machine_pin_type) )
    {
        machine_pin_obj_t * pin_obj = (machine_pin_obj_t *)(user_obj);
        for (uint32_t i = 0u; i < machine_adc_channel_num; i++)
        {
            if (   (pin_obj->gpio_port == machine_adc_objs[i]->pin_obj->gpio_port)
                && (pin_obj->gpio_pin  == machine_adc_objs[i]->pin_obj->gpio_pin)  )
            {
                return machine_adc_objs[i];
            }
        }
    }

    /* 如果传入参数是一个字符串，则通过这个字符串在Pin清单中匹配引脚名字，拿着找到的pin去匹配预定义DAC对象实例绑定的引脚。 */
    const machine_pin_obj_t *pin_obj = pin_find_by_name(&machine_pin_board_pins_locals_dict, user_obj);
    if ( pin_obj )
    {
        for (uint32_t i = 0u; i < machine_adc_channel_num; i++)
        {
            if (   (pin_obj->gpio_port == machine_adc_objs[i]->pin_obj->gpio_port)
                && (pin_obj->gpio_pin  == machine_adc_objs[i]->pin_obj->gpio_pin)  )
            {
                return machine_adc_objs[i];
            }
        }
    }

    mp_raise_ValueError(MP_ERROR_TEXT("ADC doesn't exist"));
}

/* EOF. */

